/****************************************************************************
 *
 * Copyright 2016 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/************************************************************************************
 * Serial driver for 16550 UART
 *
 *   Copyright (C) 2011-2013 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ************************************************************************************/

#ifndef __INCLUDE_SERIAL_UART_16550_H
#define __INCLUDE_SERIAL_UART_16550_H

/************************************************************************************
 * Included Files
 ************************************************************************************/

#include <tinyara/config.h>

#ifdef CONFIG_16550_UART

/************************************************************************************
 * Pre-processor Definitions
 ************************************************************************************/

/* CONFIGURATION ********************************************************************/

/* Are any UARTs enabled? */

#undef HAVE_UART
#if defined(CONFIG_16550_UART0) || defined(CONFIG_16550_UART1) || \
	defined(CONFIG_16550_UART2) || defined(CONFIG_16550_UART3)
#define HAVE_UART 1
#endif

/* We need to be told the address increment between registers and the register bit
 * width.
 */

#ifndef CONFIG_16550_REGINCR
#error "CONFIG_16550_REGINCR not defined"
#endif

#if CONFIG_16550_REGINCR != 1 && CONFIG_16550_REGINCR != 2 && CONFIG_16550_REGINCR != 4
#error "CONFIG_16550_REGINCR not supported"
#endif

#ifndef CONFIG_16550_REGWIDTH
#error "CONFIG_16550_REGWIDTH not defined"
#endif

#if CONFIG_16550_REGWIDTH != 8 && CONFIG_16550_REGWIDTH != 16 && CONFIG_16550_REGWIDTH != 32
#error "CONFIG_16550_REGWIDTH not supported"
#endif

#ifndef CONFIG_16550_ADDRWIDTH
#error "CONFIG_16550_ADDRWIDTH not defined"
#endif

#if CONFIG_16550_ADDRWIDTH != 8 && CONFIG_16550_ADDRWIDTH != 16 && CONFIG_16550_ADDRWIDTH != 32
#error "CONFIG_16550_ADDRWIDTH not supported"
#endif

/* If a UART is enabled, then its base address, clock, and IRQ must also be provided */

#ifdef CONFIG_16550_UART0
#ifndef CONFIG_16550_UART0_BASE
#error "CONFIG_16550_UART0_BASE not provided"
#undef CONFIG_16550_UART0
#endif
#ifndef CONFIG_16550_UART0_CLOCK
#error "CONFIG_16550_UART0_CLOCK not provided"
#undef CONFIG_16550_UART0
#endif
#ifndef CONFIG_16550_UART0_IRQ
#error "CONFIG_16550_UART0_IRQ not provided"
#undef CONFIG_16550_UART0
#endif
#endif

#ifdef CONFIG_16550_UART1
#ifndef CONFIG_16550_UART1_BASE
#error "CONFIG_16550_UART1_BASE not provided"
#undef CONFIG_16550_UART1
#endif
#ifndef CONFIG_16550_UART1_CLOCK
#error "CONFIG_16550_UART1_CLOCK not provided"
#undef CONFIG_16550_UART1
#endif
#ifndef CONFIG_16550_UART1_IRQ
#error "CONFIG_16550_UART1_IRQ not provided"
#undef CONFIG_16550_UART1
#endif
#endif

#ifdef CONFIG_16550_UART2
#ifndef CONFIG_16550_UART2_BASE
#error "CONFIG_16550_UART2_BASE not provided"
#undef CONFIG_16550_UART2
#endif
#ifndef CONFIG_16550_UART2_CLOCK
#error "CONFIG_16550_UART2_CLOCK not provided"
#undef CONFIG_16550_UART2
#endif
#ifndef CONFIG_16550_UART2_IRQ
#error "CONFIG_16550_UART2_IRQ not provided"
#undef CONFIG_16550_UART2
#endif
#endif

#ifdef CONFIG_16550_UART3
#ifndef CONFIG_16550_UART3_BASE
#error "CONFIG_16550_UART3_BASE not provided"
#undef CONFIG_16550_UART3
#endif
#ifndef CONFIG_16550_UART3_CLOCK
#error "CONFIG_16550_UART3_CLOCK not provided"
#undef CONFIG_16550_UART3
#endif
#ifndef CONFIG_16550_UART3_IRQ
#error "CONFIG_16550_UART3_IRQ not provided"
#undef CONFIG_16550_UART3
#endif
#endif

/* Is there a serial console? There should be at most one defined.  It could be on
 * any UARTn, n=0,1,2,3
 */

#if defined(CONFIG_16550_UART0_SERIAL_CONSOLE) && defined(CONFIG_16550_UART0)
#undef CONFIG_16550_UART1_SERIAL_CONSOLE
#undef CONFIG_16550_UART2_SERIAL_CONSOLE
#undef CONFIG_16550_UART3_SERIAL_CONSOLE
#define HAVE_16550_CONSOLE 1
#elif defined(CONFIG_16550_UART1_SERIAL_CONSOLE) && defined(CONFIG_16550_UART1)
#undef CONFIG_16550_UART0_SERIAL_CONSOLE
#undef CONFIG_16550_UART2_SERIAL_CONSOLE
#undef CONFIG_16550_UART3_SERIAL_CONSOLE
#define HAVE_16550_CONSOLE 1
#elif defined(CONFIG_16550_UART2_SERIAL_CONSOLE) && defined(CONFIG_16550_UART2)
#undef CONFIG_16550_UART0_SERIAL_CONSOLE
#undef CONFIG_16550_UART1_SERIAL_CONSOLE
#undef CONFIG_16550_UART3_SERIAL_CONSOLE
#define HAVE_16550_CONSOLE 1
#elif defined(CONFIG_16550_UART3_SERIAL_CONSOLE) && defined(CONFIG_16550_UART3)
#undef CONFIG_16550_UART0_SERIAL_CONSOLE
#undef CONFIG_16550_UART1_SERIAL_CONSOLE
#undef CONFIG_16550_UART2_SERIAL_CONSOLE
#define HAVE_16550_CONSOLE 1
#else
#undef CONFIG_16550_UART0_SERIAL_CONSOLE
#undef CONFIG_16550_UART1_SERIAL_CONSOLE
#undef CONFIG_16550_UART2_SERIAL_CONSOLE
#undef CONFIG_16550_UART3_SERIAL_CONSOLE
#undef HAVE_16550_CONSOLE
#endif

/* Register offsets *****************************************************************/

#define UART_RBR_INCR          0	/* (DLAB =0) Receiver Buffer Register */
#define UART_THR_INCR          0	/* (DLAB =0) Transmit Holding Register */
#define UART_DLL_INCR          0	/* (DLAB =1) Divisor Latch LSB */
#define UART_DLM_INCR          1	/* (DLAB =1) Divisor Latch MSB */
#define UART_IER_INCR          1	/* (DLAB =0) Interrupt Enable Register */
#define UART_IIR_INCR          2	/* Interrupt ID Register */
#define UART_FCR_INCR          2	/* FIFO Control Register */
#define UART_LCR_INCR          3	/* Line Control Register */
#define UART_MCR_INCR          4	/* Modem Control Register */
#define UART_LSR_INCR          5	/* Line Status Register */
#define UART_MSR_INCR          6	/* Modem Status Register */
#define UART_SCR_INCR          7	/* Scratch Pad Register */

#define UART_RBR_OFFSET        (CONFIG_16550_REGINCR*UART_RBR_INCR)
#define UART_THR_OFFSET        (CONFIG_16550_REGINCR*UART_THR_INCR)
#define UART_DLL_OFFSET        (CONFIG_16550_REGINCR*UART_DLL_INCR)
#define UART_DLM_OFFSET        (CONFIG_16550_REGINCR*UART_DLM_INCR)
#define UART_IER_OFFSET        (CONFIG_16550_REGINCR*UART_IER_INCR)
#define UART_IIR_OFFSET        (CONFIG_16550_REGINCR*UART_IIR_INCR)
#define UART_FCR_OFFSET        (CONFIG_16550_REGINCR*UART_FCR_INCR)
#define UART_LCR_OFFSET        (CONFIG_16550_REGINCR*UART_LCR_INCR)
#define UART_MCR_OFFSET        (CONFIG_16550_REGINCR*UART_MCR_INCR)
#define UART_LSR_OFFSET        (CONFIG_16550_REGINCR*UART_LSR_INCR)
#define UART_MSR_OFFSET        (CONFIG_16550_REGINCR*UART_MSR_INCR)
#define UART_SCR_OFFSET        (CONFIG_16550_REGINCR*UART_SCR_INCR)

/* Register bit definitions *********************************************************/

/* RBR (DLAB =0) Receiver Buffer Register */

#define UART_RBR_MASK                (0xff)	/* Bits 0-7: Oldest received byte in RX FIFO */
/* Bits 8-31: Reserved */

/* THR (DLAB =0) Transmit Holding Register */

#define UART_THR_MASK                (0xff)	/* Bits 0-7: Adds byte to TX FIFO */
/* Bits 8-31: Reserved */

/* DLL (DLAB =1) Divisor Latch LSB */

#define UART_DLL_MASK                (0xff)	/* Bits 0-7: DLL */
/* Bits 8-31: Reserved */

/* DLM (DLAB =1) Divisor Latch MSB */

#define UART_DLM_MASK                (0xff)	/* Bits 0-7: DLM */
/* Bits 8-31: Reserved */

/* IER (DLAB =0) Interrupt Enable Register */

#define UART_IER_ERBFI               (1 << 0)	/* Bit 0: Enable received data available interrupt */
#define UART_IER_ETBEI               (1 << 1)	/* Bit 1: Enable THR empty interrupt */
#define UART_IER_ELSI                (1 << 2)	/* Bit 2: Enable receiver line status interrupt */
#define UART_IER_EDSSI               (1 << 3)	/* Bit 3: Enable MODEM status interrupt */
/* Bits 4-7: Reserved */
#define UART_IER_ALLIE               (0x0f)

/* IIR Interrupt ID Register */

#define UART_IIR_INTSTATUS           (1 << 0)	/* Bit 0:  Interrupt status (active low) */
#define UART_IIR_INTID_SHIFT         (1)	/* Bits 1-3: Interrupt identification */
#define UART_IIR_INTID_MASK          (7 << UART_IIR_INTID_SHIFT)
#define UART_IIR_INTID_MSI         (0 << UART_IIR_INTID_SHIFT)	/* Modem Status */
#define UART_IIR_INTID_THRE        (1 << UART_IIR_INTID_SHIFT)	/* THR Empty Interrupt */
#define UART_IIR_INTID_RDA         (2 << UART_IIR_INTID_SHIFT)	/* Receive Data Available (RDA) */
#define UART_IIR_INTID_RLS         (3 << UART_IIR_INTID_SHIFT)	/* Receiver Line Status (RLS) */
#define UART_IIR_INTID_CTI         (6 << UART_IIR_INTID_SHIFT)	/* Character Time-out Indicator (CTI) */
/* Bits 4-5: Reserved */
#define UART_IIR_FIFOEN_SHIFT        (6)	/* Bits 6-7: RCVR FIFO interrupt */
#define UART_IIR_FIFOEN_MASK         (3 << UART_IIR_FIFOEN_SHIFT)

/* FCR FIFO Control Register */

#define UART_FCR_FIFOEN              (1 << 0)	/* Bit 0:  Enable FIFOs */
#define UART_FCR_RXRST               (1 << 1)	/* Bit 1:  RX FIFO Reset */
#define UART_FCR_TXRST               (1 << 2)	/* Bit 2:  TX FIFO Reset */
#define UART_FCR_DMAMODE             (1 << 3)	/* Bit 3:  DMA Mode Select */
/* Bits 4-5: Reserved */
#define UART_FCR_RXTRIGGER_SHIFT     (6)	/* Bits 6-7: RX Trigger Level */
#define UART_FCR_RXTRIGGER_MASK      (3 << UART_FCR_RXTRIGGER_SHIFT)
#define UART_FCR_RXTRIGGER_1       (0 << UART_FCR_RXTRIGGER_SHIFT)	/*  Trigger level 0 (1 character) */
#define UART_FCR_RXTRIGGER_4       (1 << UART_FCR_RXTRIGGER_SHIFT)	/* Trigger level 1 (4 characters) */
#define UART_FCR_RXTRIGGER_8       (2 << UART_FCR_RXTRIGGER_SHIFT)	/* Trigger level 2 (8 characters) */
#define UART_FCR_RXTRIGGER_14      (3 << UART_FCR_RXTRIGGER_SHIFT)	/* Trigger level 3 (14 characters) */

/* LCR Line Control Register */

#define UART_LCR_WLS_SHIFT           (0)	/* Bit 0-1: Word Length Select */
#define UART_LCR_WLS_MASK            (3 << UART_LCR_WLS_SHIFT)
#define UART_LCR_WLS_5BIT          (0 << UART_LCR_WLS_SHIFT)
#define UART_LCR_WLS_6BIT          (1 << UART_LCR_WLS_SHIFT)
#define UART_LCR_WLS_7BIT          (2 << UART_LCR_WLS_SHIFT)
#define UART_LCR_WLS_8BIT          (3 << UART_LCR_WLS_SHIFT)
#define UART_LCR_STB                 (1 << 2)	/* Bit 2:  Number of Stop Bits */
#define UART_LCR_PEN                 (1 << 3)	/* Bit 3:  Parity Enable */
#define UART_LCR_EPS                 (1 << 4)	/* Bit 4:  Even Parity Select */
#define UART_LCR_STICKY              (1 << 5)	/* Bit 5:  Stick Parity */
#define UART_LCR_BRK                 (1 << 6)	/* Bit 6: Break Control */
#define UART_LCR_DLAB                (1 << 7)	/* Bit 7: Divisor Latch Access Bit (DLAB) */

/* MCR Modem Control Register */

#define UART_MCR_DTR                 (1 << 0)	/* Bit 0:  DTR Control Source for DTR output */
#define UART_MCR_RTS                 (1 << 1)	/* Bit 1:  Control Source for  RTS output */
#define UART_MCR_OUT1                (1 << 2)	/* Bit 2:  Auxiliary user-defined output 1 */
#define UART_MCR_OUT2                (1 << 3)	/* Bit 3:  Auxiliary user-defined output 2 */
#define UART_MCR_LPBK                (1 << 4)	/* Bit 4:  Loopback Mode Select */
/* Bit 5-7: Reserved */

/* LSR Line Status Register */

#define UART_LSR_DR                  (1 << 0)	/* Bit 0:  Data Ready */
#define UART_LSR_OE                  (1 << 1)	/* Bit 1:  Overrun Error */
#define UART_LSR_PE                  (1 << 2)	/* Bit 2:  Parity Error */
#define UART_LSR_FE                  (1 << 3)	/* Bit 3:  Framing Error */
#define UART_LSR_BI                  (1 << 4)	/* Bit 4:  Break Interrupt */
#define UART_LSR_THRE                (1 << 5)	/* Bit 5:  Transmitter Holding Register Empty */
#define UART_LSR_TEMT                (1 << 6)	/* Bit 6:  Transmitter Empty */
#define UART_LSR_RXFE                (1 << 7)	/* Bit 7:  Error in RX FIFO (RXFE) */

/* SCR Scratch Pad Register */

#define UART_SCR_MASK                (0xff)	/* Bits 0-7: SCR data */

/************************************************************************************
 * Public Types
 ************************************************************************************/

#if CONFIG_16550_REGWIDTH == 8
typedef uint8_t uart_datawidth_t;
#elif CONFIG_16550_REGWIDTH == 16
typedef uint16_t uart_datawidth_t;
#elif CONFIG_16550_REGWIDTH == 32
typedef uint32_t uart_datawidth_t;
#endif

#if CONFIG_16550_ADDRWIDTH == 8
typedef uint8_t uart_addrwidth_t;
#elif CONFIG_16550_ADDRWIDTH == 16
typedef uint16_t uart_addrwidth_t;
#elif CONFIG_16550_ADDRWIDTH == 32
typedef uint32_t uart_addrwidth_t;
#endif

/************************************************************************************
 * Public Data
 ************************************************************************************/

/************************************************************************************
 * Public Functions
 ************************************************************************************/

/************************************************************************************
 * Name: uart_getreg(), uart_putreg()
 *
 * Description:
 *   These functions must be provided by the processor-specific code in order to
 *   correctly access 16550 registers
 *
 ************************************************************************************/

extern uart_datawidth_t uart_getreg(uart_addrwidth_t base, unsigned int offset);
extern void uart_putreg(uart_addrwidth_t base, unsigned int offset, uart_datawidth_t value);

#endif							/* CONFIG_16550_UART */
#endif							/* __INCLUDE_SERIAL_UART_16550_H */
